r"""
Tutorial: visualizing root systems

Root systems encode the positions of collections of hyperplanes in
space, and form the fundamental combinatorial data underlying Coxeter
and Weyl groups, Lie algebras and groups, etc. The theory can be a bit
intimidating at first because of the many technical gadgets (roots,
coroots, weights, ...). Vizualizing them goes a long way toward
building a geometric intuition.

This tutorial starts from simple plots and guides you all the way to
advanced plots with your own combinatorial data drawn on top of it.

.. SEEALSO::

    - :ref:`sage.combinat.root_system.root_system`
      -- An overview of root systems in Sage

    - :meth:`RootLatticeRealizations.ParentMethods.plot()
      <sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot>`
      -- the main plotting function, with pointers to all the subroutines


First plots
-----------

In this first plot, we draw the root system for type `A_2` in the
ambient space. It is generated from two hyperplanes at a 120 degree
angle::

    sage: L = RootSystem(["A",2]).ambient_space()
    sage: L.plot()
    Graphics object consisting of 13 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2]).ambient_space()
    sphinx_plot(L.plot())

Each of those hyperplane `H_{\alpha^\vee_i}` is described by a linear
form `\alpha_i^\vee` called simple coroot. To each such hyperplane
corresponds a reflection along a vector called root. In this picture,
the reflections are orthogonal and the two simple roots `\alpha_1` and
`\alpha_2` are vectors which are normal to the reflection hyperplanes.
The same color code is used uniformly: blue for 1, red for 2, green
for 3, ... (see :meth:`CartanType.color()
<sage.combinat.root_system.cartan_type.CartanTypeFactory.color>`). The
fundamental weights, `\Lambda_1` and `\Lambda_2` form the dual basis of
the coroots.

The two reflections generate a group of order six which is nothing but
the usual symmetric group `S_3`, in its natural action by permutations
of the coordinates of the ambient space. Wait, but the ambient space
should be of dimension `3` then? That's perfectly right. Here is the
full picture in 3D::

    sage: L = RootSystem(["A",2]).ambient_space()
    sage: L.plot(projection=False)
    Graphics3d Object

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2]).ambient_space()
    sphinx_plot(L.plot(projection=False))

However in this space, the line `(1,1,1)` is fixed by the action of
the group. Therefore, the so called barycentric projection orthogonal
to `(1,1,1)` gives a convenient 2D picture which contains all the
essential information. The same projection is used by default in type
`G_2`::

    sage: L = RootSystem(["G",2]).ambient_space()
    sage: L.plot(reflection_hyperplanes="all")
    Graphics object consisting of 21 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["G",2]).ambient_space()
    sphinx_plot(L.plot(reflection_hyperplanes="all"))

The group is now the dihedral group of order 12, generated by the two
reflections `s_1` and `s_2`. The picture displays the hyperplanes for
all 12 reflections of the group. Those reflections delimit 12 chambers
which are in one to one correspondence with the elements of the
group. The fundamental chamber, which is grayed out, is associated
with the identity of the group.

.. WARNING::

    The fundamental chamber is currently plotted as the cone generated
    by the fundamental weights. As can be seen on the previous 3D
    picture this is not quite correct if the fundamental weights do
    not span the space.

    Another caveat is that some plotting features may require
    manipulating elements with rational coordinates which will fail if
    one is working in, say, the weight lattice. It is therefore
    recommended to use the root, weight, or ambient spaces for
    plotting purposes rather than their lattice counterparts.

Coming back to the symmetric group, here is the picture in the weight
space, with all roots and all reflection hyperplanes; remark that,
unlike in the ambient space, a root is not necessarily orthogonal to
its corresponding reflection hyperplane::

    sage: L = RootSystem(["A",2]).weight_space()
    sage: L.plot(roots="all", reflection_hyperplanes="all").show(figsize=15)

.. NOTE::

    Setting a larger figure size as above can help reduce
    the overlap between the text labels when the figure
    gets crowded.

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2]).weight_space()
    sphinx_plot(L.plot(roots="all", reflection_hyperplanes="all"))

One can further customize which roots to display, as in
the following example showing the positive roots in the
weight space for type ['G',2], labelled by their
coordinates in the root lattice::

    sage: Q = RootSystem(["G",2]).root_space()
    sage: L = RootSystem(["G",2]).ambient_space()
    sage: L.plot(roots=list(Q.positive_roots()), fundamental_weights=False)
    Graphics object consisting of 17 graphics primitives

.. PLOT::
    :width: 300 px

    Q = RootSystem(["G",2]).root_space()
    L = RootSystem(["G",2]).ambient_space()
    sphinx_plot(L.plot(roots=list(Q.positive_roots()), fundamental_weights=False))

One can also customize the projection by specifying a function. Here,
we display all the roots for type `E_8` using the projection from its
eight dimensional ambient space onto 3D described on
:wikipedia:`Wikipedia%27s E8 3D picture <File:E8_3D.png>`::

    sage: M = matrix([[0., -0.556793440452, 0.19694925177, -0.19694925177, 0.0805477263944, -0.385290876171, 0., 0.385290876171],
    ....:             [0.180913155536, 0., 0.160212955043, 0.160212955043, 0., 0.0990170516545, 0.766360424875, 0.0990170516545],
    ....:             [0.338261212718, 0, 0, -0.338261212718, 0.672816364803, 0.171502564281, 0, -0.171502564281]])
    sage: L = RootSystem(["E",8]).ambient_space()
    sage: L.dimension()
    8
    sage: L.plot(roots="all", reflection_hyperplanes=False, projection=lambda v: M*vector(v), labels=False) # long time
    Graphics3d Object

.. PLOT::
    :width: 300 px

    M = matrix([[0., -0.556793440452, 0.19694925177, -0.19694925177, 0.0805477263944, -0.385290876171, 0., 0.385290876171],
                [0.180913155536, 0., 0.160212955043, 0.160212955043, 0., 0.0990170516545, 0.766360424875, 0.0990170516545],
                [0.338261212718, 0, 0, -0.338261212718, 0.672816364803, 0.171502564281, 0, -0.171502564281]])
    L = RootSystem(["E",8]).ambient_space()
    sphinx_plot(L.plot(roots="all", reflection_hyperplanes=False, projection=lambda v: M*vector(v), labels=False))

The projection function should be linear or affine, and return a
vector with rational coordinates. The rationale for the later
constraint is to allow for using the PPL exact library for
manipulating polytopes. Indeed exact calculations give cleaner
pictures (adjacent objects, intersection with the bounding box, ...).
Besides the interface to PPL is indeed currently faster than that for
CDD, and it is likely to become even more so.

.. TOPIC:: Exercise

    Draw all finite root systems in 2D, using the canonical projection
    onto their Coxeter plane. See
    `Stembridge's page <http://www.math.lsa.umich.edu/~jrs/coxplane.html>`_.


Alcoves and chambers
--------------------

We now draw the root system for type `G_2`, with its alcoves (in
finite type, those really are the chambers) and the corresponding
elements of the Weyl group. We enlarge a bit the bounding box to make
sure everything fits in the picture::

    sage: RootSystem(["G",2]).ambient_space().plot(alcoves=True, alcove_labels=True, bounding_box=5)
    Graphics object consisting of 37 graphics primitives

.. PLOT::
    :width: 300 px

    sphinx_plot(RootSystem(["G",2]).ambient_space().plot(alcoves=True, alcove_labels=True, bounding_box=5))

The same picture in 3D, for type `B_3`::

    sage: RootSystem(["B",3]).ambient_space().plot(alcoves=True, alcove_labels=True)
    Graphics3d Object

.. PLOT::
    :width: 300 px

    sphinx_plot(RootSystem(["B",3]).ambient_space().plot(alcoves=True, alcove_labels=True))

.. TOPIC:: Exercise

    Can you spot the fundamental chamber? The fundamental weights? The
    simple roots? The longest element of the Weyl group?

Alcove pictures for affine types
--------------------------------

We now draw the usual alcove picture for affine type `A_2^{(1)}`::

    sage: L = RootSystem(["A",2,1]).ambient_space()
    sage: L.plot()                                     # long time
    Graphics object consisting of 160 graphics primitives

.. PLOT::
    :width: 300 px

    sphinx_plot(RootSystem(["A",2,1]).ambient_space().plot())

This picture is convenient because it is low dimensional and contains
most of the relevant information. Beside, by choosing the ambient
space, the elements of the Weyl group act as orthogonal affine
maps. In particular, reflections are usual (affine) orthogonal
reflections. However this is in fact only a slice of the real picture:
the Weyl group actually acts by linear maps on the full ambient
space. Those maps stabilize the so-called level `l` hyperplanes, and
we are visualizing here what's happening at level `1`. Here is the
full picture in 3D::

    sage: L.plot(bounding_box=[[-3,3],[-3,3],[-1,1]], affine=False) # long time
    Graphics3d Object

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2,1]).ambient_space()
    sphinx_plot(L.plot(bounding_box=[[-3,3],[-3,3],[-1,1]], affine=False))

In fact, in type `A`, this really is a picture in 4D, but as usual the
barycentric projection kills the boring extra dimension for us.

It's usually more readable to only draw the intersection of the
reflection hyperplanes with the level `1` hyperplane::

    sage: L.plot(affine=False, level=1)                # long time
    Graphics3d Object

.. PLOT::
    :width: 300 px

    sphinx_plot(RootSystem(["A",2,1]).ambient_space().plot(affine=False, level=1))

Such 3D pictures are useful to better understand technicalities, like
the fact that the fundamental weights do not necessarily all live at
level 1::

    sage: L = RootSystem(["G",2,1]).ambient_space()
    sage: L.plot(affine=False, level=1)
    Graphics3d Object

.. PLOT::
    :width: 300 px

    sphinx_plot(RootSystem(["G",2,1]).ambient_space().plot(affine=False, level=1))

.. NOTE::

    Such pictures may tend to be a bit flat, and it may be helpful to
    play with the aspect_ratio and more generally with the various
    options of the :meth:`~sage.plot.plot3d.base.Graphics3d.show`
    method::

        sage: p = L.plot(affine=False, level=1)
        sage: p.show(aspect_ratio=[1,1,2], frame=False)

.. TOPIC:: Exercise

    Draw the alcove picture at level 1, and compare the position of
    the fundamental weights and the vertices of the fundamental
    alcove.

As for finite root systems, the alcoves are indexed by the elements of
the Weyl group `W`. Two alcoves indexed by `u` and `v` respectively
share a wall if `u` and `v` are neighbors in the right Cayley graph:
`u = vs_i`; the color of that wall is given by `i`::

    sage: L = RootSystem(["C",2,1]).ambient_space()
    sage: L.plot(coroots="simple", alcove_labels=True) # long time
    Graphics object consisting of 216 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["C",2,1]).ambient_space()
    sphinx_plot(L.plot(coroots="simple", alcove_labels=True))

Even 2D pictures of the rank `1 + 1` cases can give some food for
thought. Here, we draw the root lattice, with the positive roots of
small height in the root poset::

    sage: L = RootSystem(["A",1,1]).root_lattice()
    sage: seed = L.simple_roots()
    sage: succ = attrcall("pred")
    sage: positive_roots = RecursivelyEnumeratedSet(seed, succ, structure='graded')
    sage: it = iter(positive_roots)
    sage: first_positive_roots = [next(it) for i in range(10)]
    sage: L.plot(roots=first_positive_roots, affine=False, alcoves=False)
    Graphics object consisting of 24 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",1,1]).root_lattice()
    seed = L.simple_roots()
    succ = attrcall("pred")
    positive_roots = RecursivelyEnumeratedSet(seed, succ, structure='graded')
    it = iter(positive_roots)
    first_positive_roots = [next(it) for i in range(10)]
    sphinx_plot(L.plot(roots=first_positive_roots, affine=False, alcoves=False))

.. TOPIC:: Exercises

    #. Use the same trick to draw the reflection hyperplanes in the
       weight lattice for the coroots of small height. Add the
       indexing of the alcoves by elements of the Weyl group.
       See below for a solution.

    #. Draw the positive roots in the weight lattice and in the
       extended weight lattice.

    #. Draw the reflection hyperplanes in the root lattice

    #. Recreate John Stembridge's
       `"Sandwich" arrangement pictures <http://www.math.lsa.umich.edu/~jrs/archive.html>`_
       by choosing appropriate coroots for the reflection hyperplanes.

Here is a polished solution for the first exercise::

    sage: L = RootSystem(["A",1,1]).weight_space()
    sage: seed = L.simple_coroots()
    sage: succ = attrcall("pred")
    sage: positive_coroots = RecursivelyEnumeratedSet(seed, succ, structure='graded')
    sage: it = iter(positive_coroots)
    sage: first_positive_coroots = [next(it) for i in range(20)]
    sage: p = L.plot(fundamental_chamber=True, reflection_hyperplanes=first_positive_coroots,
    ....:            affine=False, alcove_labels=1,
    ....:            bounding_box=[[-9,9],[-1,2]],
    ....:            projection=lambda x: matrix([[1,-1],[1,1]])*vector(x))
    sage: p.show(figsize=20)                           # long time


Higher dimension affine pictures
--------------------------------

We now do some plots for rank 4 affine types, at level 1. The space is
tiled by the alcoves, each of which is a 3D simplex::

    sage: L = RootSystem(["A",3,1]).ambient_space()
    sage: L.plot(reflection_hyperplanes=False, bounding_box=85/100) # long time
    Graphics3d Object

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",3,1]).ambient_space()
    sphinx_plot(L.plot(reflection_hyperplanes=False, bounding_box=Rational((85,100))))

It is recommended to use a small bounding box here, for otherwise the
number of simplices grows quicker than what Sage can handle
smoothly. It can help to specify explicitly which alcoves to
visualize. Here is the fundamental alcove, specified by an element of
the Weyl group::

    sage: W = L.weyl_group()
    sage: L.plot(reflection_hyperplanes=False, alcoves=[W.one()], bounding_box=2)
    Graphics3d Object

.. PLOT::
    :width: 300 px

    X = RootSystem(["A",3,1]).ambient_space()
    XW = X.weyl_group()
    sphinx_plot(X.plot(reflection_hyperplanes=False, alcoves=[XW.one()], bounding_box=2))

and the fundamental polygon, specified by the coordinates of its
center in the root lattice::

    sage: W = L.weyl_group()
    sage: L.plot(reflection_hyperplanes=False, alcoves=[[0,0]], bounding_box=2)
    Graphics3d Object

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",3,1]).ambient_space()
    W = L.weyl_group()
    sphinx_plot(L.plot(reflection_hyperplanes=False, alcoves=[[0,0]], bounding_box=2))

Finally, we draw the alcoves in the classical fundamental chambers,
using that those are indexed by the elements of the Weyl group having
no other left descent than `0`. In order to see the inner structure,
we only draw the wireframe of the facets of the alcoves. Specifying
the ``wireframe`` option requires a more flexible syntax for plots
which will be explained later on in this tutorial::

    sage: L = RootSystem(["B",3,1]).ambient_space()
    sage: W = L.weyl_group()
    sage: alcoves = [~w for d in range(12) for w in W.affine_grassmannian_elements_of_given_length(d)]
    sage: p = L.plot_fundamental_chamber("classical")
    sage: p += L.plot_alcoves(alcoves=alcoves, wireframe=True)
    sage: p += L.plot_fundamental_weights()
    sage: p.show(frame=False)

.. PLOT::
    :width: 300 px

    L = RootSystem(["B",3,1]).ambient_space()
    W = L.weyl_group()
    alcoves = [~w for d in range(12) for w in W.affine_grassmannian_elements_of_given_length(d)]
    p = L.plot_fundamental_chamber("classical")
    p += L.plot_alcoves(alcoves=alcoves, wireframe=True)
    p += L.plot_fundamental_weights()
    sphinx_plot(p)

.. TOPIC:: Exercises

    #. Draw the fundamental alcove in the ambient space, just by
       itself (no reflection hyperplane, root, ...).  The automorphism
       group of the Dynkin diagram for `A_3^{(1)}` (a cycle of length 4)
       is the dihedral group. Visualize the corresponding symmetries
       of the fundamental alcove.

    #. Draw the fundamental alcoves for the other rank 4 affine types,
       and recover the automorphism groups of their Dynkin diagram
       from the pictures.

Drawing on top of a root system plot
------------------------------------

The root system plots have been designed to be used as wallpaper on
top of which to draw more information. In the following example, we
draw an alcove walk, specified by a word of indices of simple
reflections, on top of the weight lattice in affine type `A_{2,1}`::

    sage: L = RootSystem(["A",2,1]).ambient_space()
    sage: w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
    sage: L.plot(alcove_walk=w1, bounding_box=6)       # long time
    Graphics object consisting of 535 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2,1]).ambient_space()
    w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
    sphinx_plot(L.plot(alcove_walk=w1, bounding_box=6))

Now, what about drawing several alcove walks, and specifying some
colors? A single do-it-all plot method would be cumbersome; so
instead, it is actually built on top of many methods (see the list
below) that can be called independently and combined at will::

    sage: L.plot_roots() + L.plot_reflection_hyperplanes()
    Graphics object consisting of 12 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2,1]).ambient_space()
    w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
    sphinx_plot(L.plot_roots() + L.plot_reflection_hyperplanes())

.. NOTE::

    By default the axes are disabled in root system plots since they
    tend to pollute the picture. Annoyingly they come back when
    combining them. Here is a workaround::

        sage: p = L.plot_roots() + L.plot_reflection_hyperplanes()
        sage: p.axes(False)
        sage: p
        Graphics object consisting of 12 graphics primitives

    .. PLOT::
        :width: 300 px

        L = RootSystem(["A",2,1]).ambient_space()
        p = L.plot_roots() + L.plot_reflection_hyperplanes()
        p.axes(False)
        sphinx_plot(p)

In order to specify common information for all the pieces of a root
system plot (choice of projection, bounding box, color code for the
index set, ...), the easiest is to create an option object using
:meth:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot_parse_options`,
and pass it down to each piece. We use this to plot our two walks::

    sage: plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,6]])
    sage: w2 = [2,1,2,0,2,0,2,1,2,0,1,2,1,2,1,0,1,2,0,2,0,1,2,0,2]
    sage: p = L.plot_alcoves(plot_options=plot_options) # long time
    sage: p += L.plot_alcove_walk(w1, color="green", plot_options=plot_options) # long time
    sage: p += L.plot_alcove_walk(w2, color="orange", plot_options=plot_options) # long time
    sage: p # long time
    Graphics object consisting of ... graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2,1]).ambient_space()
    w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
    plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,6]])
    w2 = [2,1,2,0,2,0,2,1,2,0,1,2,1,2,1,0,1,2,0,2,0,1,2,0,2]
    sphinx_plot(L.plot_alcoves(plot_options=plot_options)
                + L.plot_alcove_walk(w1, color="green", plot_options=plot_options)
                + L.plot_alcove_walk(w2, color="orange", plot_options=plot_options))

And another with some foldings::

    sage: p += L.plot_alcove_walk([0,1,2,0,2,0,1,2,0,1],
    ....:                         foldings=[False, False, True, False, False, False, True, False, True, False],
    ....:                         color="purple")
    sage: p.axes(False)
    sage: p.show(figsize=20)

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2,1]).ambient_space()
    w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
    plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,6]])
    w2 = [2,1,2,0,2,0,2,1,2,0,1,2,1,2,1,0,1,2,0,2,0,1,2,0,2]
    p = L.plot_alcoves(plot_options=plot_options)
    p += L.plot_alcove_walk(w1, color="green", plot_options=plot_options)
    p += L.plot_alcove_walk(w2, color="orange", plot_options=plot_options)
    p += L.plot_alcove_walk([0,1,2,0,2,0,1,2,0,1],
                             foldings=[False, False, True, False, False, False, True, False, True, False],
                             color="purple")
    p.axes(False)
    sphinx_plot(p)

Here we show a weight at level `0` and the reduced word implementing
the translation by this weight::

    sage: L = RootSystem(["A",2,1]).ambient_space()
    sage: P = RootSystem(["A",2,1]).weight_space(extended=True)
    sage: Lambda = P.fundamental_weights()
    sage: t = 6*Lambda[1] - 2*Lambda[2] - 4*Lambda[0]
    sage: walk = L.reduced_word_of_translation(L(t))
    sage: plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,5]])
    sage: p = L.plot(plot_options=plot_options)        # long time
    sage: p += L.plot_alcove_walk(walk, color="green", plot_options=plot_options) # long time
    sage: p += plot_options.family_of_vectors({t: L(t)}) # long time
    sage: plot_options.finalize(p) # long time
    Graphics object consisting of ... graphics primitives
    sage: p # long time
    Graphics object consisting of ... graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2,1]).ambient_space()
    P = RootSystem(["A",2,1]).weight_space(extended=True)
    Lambda = P.fundamental_weights()
    t = 6*Lambda[1] - 2*Lambda[2] - 4*Lambda[0]
    walk = L.reduced_word_of_translation(L(t))
    plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,5]])
    p = L.plot(plot_options=plot_options)        # long time
    p += L.plot_alcove_walk(walk, color="green", plot_options=plot_options)
    p += plot_options.family_of_vectors({t: L(t)})
    plot_options.finalize(p)
    sphinx_plot(p)

Note that the coloring of the translated alcove does not match with
that of the fundamental alcove: the translation actually lives in the
extended Weyl group and is the composition of the simple reflections
indexed by the alcove walk together with a rotation implementing an
automorphism of the Dynkin diagram.

We conclude with a rank `3 + 1` alcove walk::

    sage: L = RootSystem(["B",3,1]).ambient_space()
    sage: w3 = [0,2,1,3,2,0,2,1,0,2,3,1,2,1,3,2,0,2,0,1,2,0]
    sage: L.plot_fundamental_weights() + L.plot_reflection_hyperplanes(bounding_box=2) + L.plot_alcove_walk(w3)
    Graphics3d Object

.. PLOT::
    :width: 300 px

    L = RootSystem(["B",3,1]).ambient_space()
    w3 = [0,2,1,3,2,0,2,1,0,2,3,1,2,1,3,2,0,2,0,1,2,0]
    sphinx_plot(L.plot_fundamental_weights()
                + L.plot_reflection_hyperplanes(bounding_box=2)
                + L.plot_alcove_walk(w3))

.. TOPIC:: Exercise

    #. Draw the tiling of 3D space by the fundamental polygons for
       types A,B,C,D. Hints: use the ``wireframe`` option of
       :meth:`RootLatticeRealizations.ParentMethods.plot_alcoves` and
       the ``color`` option of :meth:`plot` to only draw the alcove
       facets indexed by `0`.

.. TOPIC:: Solution

    ::

        sage: L = RootSystem(["A",3,1]).ambient_space()
        sage: alcoves = cartesian_product([[0,1],[0,1],[0,1]])
        sage: color = lambda i: "black" if i==0 else None
        sage: L.plot_alcoves(alcoves=alcoves, color=color, bounding_box=10,wireframe=True).show(frame=False) # long time

    .. PLOT::
        :width: 300 px

        L = RootSystem(["A",3,1]).ambient_space()
        alcoves = cartesian_product([[0,1],[0,1],[0,1]])
        color = lambda i: "black" if i==0 else None
        sphinx_plot(L.plot_alcoves(alcoves=alcoves, color=color, bounding_box=10,wireframe=True))

Hand drawing on top of a root system plot (aka Coxeter graph paper)
-------------------------------------------------------------------

Taken from John Stembridge's excellent
`data archive <http://www.math.lsa.umich.edu/~jrs/archive.html>`_:

"If you've ever worked with affine reflection groups, you've probably
wasted lots of time drawing the reflecting hyperplanes of the rank 2
groups on scraps of paper. You may also have wished you had pads of
graph paper with these lines drawn in for you. If so, you've come to
the right place. Behold! Coxeter graph paper!".

Now you can create your own customized color Coxeter graph paper::

    sage: L = RootSystem(["C",2,1]).ambient_space()
    sage: p = L.plot(bounding_box=[[-8,9],[-5,7]], coroots="simple") # long time (10 s)
    sage: p # long time
    Graphics object consisting of ... graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["C",2,1]).ambient_space()
    sphinx_plot(L.plot(bounding_box=[[-8,9],[-5,7]], coroots="simple"))

By default Sage's plot are bitmap pictures which would come out ugly
if printed on paper. Instead, we recommend saving the picture in
postscript or svg before printing it::

    sage: p.save("C21paper.eps")        # not tested

.. NOTE::

    Drawing pictures with a large number of alcoves is currently
    somewhat ridiculously slow. This is due to the use of generic code
    that works uniformly in all dimension rather than taylor-made code
    for 2D. Things should improve with the fast interface to the PPL
    library (see e.g. :trac:`12553`).

Drawing custom objects on top of a root system plot
---------------------------------------------------

So far so good. Now, what if one wants to draw, on top of a root
system plot, some object for which there is no preexisting plot
method? Again, the ``plot_options`` object come in handy, as it can be
used to compute appropriate coordinates. Here we draw the
permutohedron, that is the Cayley graph of the symmetric group `W`, by
positioning each element `w` at `w(\rho)`, where `\rho` is in the
fundamental alcove::

    sage: L = RootSystem(["A",2]).ambient_space()
    sage: rho = L.rho()
    sage: plot_options = L.plot_parse_options()
    sage: W = L.weyl_group()
    sage: g = W.cayley_graph(side="right")
    sage: positions = {w: plot_options.projection(w.action(rho)) for w in W}
    sage: p = L.plot_alcoves()
    sage: p += g.plot(pos = positions, vertex_size=0,
    ....:             color_by_label=plot_options.color)
    sage: p.axes(False)
    sage: p
    Graphics object consisting of 30 graphics primitives

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",2]).ambient_space()
    rho = L.rho()
    plot_options = L.plot_parse_options()
    W = L.weyl_group()
    g = W.cayley_graph(side="right")
    positions = {w: plot_options.projection(w.action(rho)) for w in W}
    p = L.plot_alcoves()
    p += g.plot(pos = positions, vertex_size=0, color_by_label=plot_options.color)
    p.axes(False)
    sphinx_plot(p)

.. TODO:: Could we have nice `\LaTeX` labels in this graph?

The same picture for `A_3` gives a nice 3D permutohedron::

    sage: L = RootSystem(["A",3]).ambient_space()
    sage: rho = L.rho()
    sage: plot_options = L.plot_parse_options()
    sage: W = L.weyl_group()
    sage: g = W.cayley_graph(side="right")
    sage: positions = {w: plot_options.projection(w.action(rho)) for w in W}
    sage: p = L.plot_roots()
    sage: p += g.plot3d(pos3d=positions, color_by_label=plot_options.color)
    sage: p
    Graphics3d Object

.. PLOT::
    :width: 300 px

    L = RootSystem(["A",3]).ambient_space()
    rho = L.rho()
    plot_options = L.plot_parse_options()
    W = L.weyl_group()
    g = W.cayley_graph(side="right")
    positions = {w: plot_options.projection(w.action(rho)) for w in W}
    sphinx_plot(L.plot_roots() + g.plot3d(pos3d=positions, color_by_label=plot_options.color))

.. TOPIC:: Exercises

    #. Locate the identity element of `W` in the previous picture

    #. Rotate the picture appropriately to highlight the
       various symmetries of the permutohedron.

    #. Make a function out of the previous example, and
       explore the Cayley graphs of all rank 2 and 3 Weyl groups.

    #. Draw the root poset for type `B_2` and `B_3`

    #. Draw the root poset for type `E_8` to recover the picture from
       :wikipedia:`File:E8_3D.png`

Similarly, we display a crystal graph by positioning each element
according to its weight::

    sage: C = crystals.Tableaux(["A",2], shape=[4,2])
    sage: L = C.weight_lattice_realization()
    sage: plot_options = L.plot_parse_options()

    sage: g = C.digraph()
    sage: positions = {x: plot_options.projection(x.weight()) for x in C}
    sage: p = L.plot()
    sage: p += g.plot(pos=positions,
    ....:             color_by_label=plot_options.color, vertex_size=0)
    sage: p.axes(False)
    sage: p.show(figsize=15)

.. PLOT::
    :width: 300 px

    C = crystals.Tableaux(["A",2], shape=[4,2])
    L = C.weight_lattice_realization()
    plot_options = L.plot_parse_options()
    g = C.digraph()
    positions = {x: plot_options.projection(x.weight()) for x in C}
    p = L.plot()
    p += g.plot(pos=positions, color_by_label=plot_options.color, vertex_size=0)
    p.axes(False)
    sphinx_plot(p)

.. NOTE::

    In the above picture, many pairs of tableaux have the
    same weight and are thus superposed (look for example
    near the center). Some more layout logic would be
    needed to separate those nodes properly, but the
    foundations are laid firmly and uniformly across all
    types of root systems for writing such extensions.

Here is an analogue picture in 3D::

    sage: C = crystals.Tableaux(["A",3], shape=[3,2,1])
    sage: L = C.weight_lattice_realization()
    sage: plot_options = L.plot_parse_options()
    sage: g = C.digraph()
    sage: positions = {x:plot_options.projection(x.weight()) for x in C}
    sage: p = L.plot(reflection_hyperplanes=False, fundamental_weights=False)
    sage: p += g.plot3d(pos3d=positions, vertex_labels=True,
    ....:               color_by_label=plot_options.color, edge_labels=True)
    sage: p
    Graphics3d Object

.. TOPIC:: Exercise

    Explore the previous picture and notice how the edges
    of the crystal graph are parallel to the simple roots.


Enjoy and please post your best pictures on the
`Sage-Combinat wiki <http://wiki.sagemath.org/combinat/CoolPictures>`_.
"""
# ****************************************************************************
#       Copyright (C) 2013 Nicolas M. Thiery <nthiery at users.sf.net>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#                  https://www.gnu.org/licenses/
# ****************************************************************************

from sage.misc.cachefunc import cached_method, cached_function
from sage.misc.latex import latex
from sage.misc.lazy_import import lazy_import
from sage.structure.element import parent
from sage.modules.free_module_element import vector
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.combinat.root_system.cartan_type import CartanType
lazy_import("sage.combinat.root_system.root_lattice_realizations",
            "RootLatticeRealizations")


class PlotOptions(object):
    r"""
    A class for plotting options for root lattice realizations.

    .. SEEALSO::

        - :meth:`RootLatticeRealizations.ParentMethods.plot()
          <sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot>`
          for a description of the plotting options
        - :ref:`sage.combinat.root_system.plot` for a tutorial on root
          system plotting
    """
    def __init__(self, space,
                 projection=True,
                 bounding_box=3,
                 color=CartanType.color,
                 labels=True,
                 level=None,
                 affine=None,
                 arrowsize=5,
                 ):
        r"""
        TESTS::

            sage: L = RootSystem(['B',2,1]).weight_space()
            sage: options = L.plot_parse_options()
            sage: options.dimension
            2
            sage: options._projections
            [Weight space over the Rational Field of the Root system of type ['B', 2],
             <bound method RootLatticeRealizations.ParentMethods._plot_projection of Weight space over the Rational Field of the Root system of type ['B', 2]>]

            sage: L = RootSystem(['B',2,1]).ambient_space()
            sage: options = L.plot_parse_options()
            sage: options.dimension
            2
            sage: options._projections
            [Ambient space of the Root system of type ['B', 2],
             <bound method RootLatticeRealizations.ParentMethods._plot_projection of Ambient space of the Root system of type ['B', 2]>]

            sage: options = L.plot_parse_options(affine=True)
            sage: options.dimension
            2
            sage: options._projections
            [Ambient space of the Root system of type ['B', 2],
             <bound method RootLatticeRealizations.ParentMethods._plot_projection of Ambient space of the Root system of type ['B', 2]>]

            sage: options = L.plot_parse_options(affine=False)
            sage: options._projections
            [<bound method AmbientSpace._plot_projection of Ambient space of the Root system of type ['B', 2, 1]>]
            sage: options.dimension
            3

            sage: options = L.plot_parse_options(affine=False, projection='barycentric')
            sage: options._projections
            [<bound method RootLatticeRealizations.ParentMethods._plot_projection_barycentric of Ambient space of the Root system of type ['B', 2, 1]>]
            sage: options.dimension
            3
        """
        self.space = space
        self._color = color
        self._arrowsize = arrowsize
        self.labels = labels

        # self.level = l != None: whether to intersect the alcove picture at level l
        # self.affine: whether to project at level l and then onto the classical space

        if affine is None:
            affine = space.cartan_type().is_affine()
        if affine:
            if level is None:
                level = 1
            if not space.cartan_type().is_affine():
                raise ValueError("affine option only valid for affine types")
            projections=[space.classical()]
            projection_space = space.classical()
        else:
            projections=[]
            projection_space = space

        self.affine = affine
        self.level = level

        if projection is True:
            projections.append(projection_space._plot_projection)
        elif projection == "barycentric":
            projections.append(projection_space._plot_projection_barycentric)
        elif projection is not False:
            # assert projection is a callable
            projections.append(projection)

        self._projections = projections

        self.origin_projected = self.projection(space.zero())

        self.dimension = len(self.origin_projected)

        # Bounding box
        from sage.rings.real_mpfr import RR
        from sage.geometry.polyhedron.all import Polyhedron
        from itertools import product
        if bounding_box in RR:
            bounding_box = [[-bounding_box,bounding_box]] * self.dimension
        else:
            if not len(bounding_box) == self.dimension:
                raise TypeError("bounding_box argument doesn't match with the plot dimension")
            elif not all(len(b)==2 for b in bounding_box):
                raise TypeError("Invalid bounding box %s"%bounding_box)
        self.bounding_box = Polyhedron(vertices=product(*bounding_box))

    @cached_method
    def in_bounding_box(self, x):
        r"""
        Return whether ``x`` is in the bounding box.

        INPUT:

        - ``x`` -- an element of the root lattice realization

        This method is currently one of the bottlenecks, and therefore
        cached.

        EXAMPLES::

            sage: L = RootSystem(["A",2,1]).ambient_space()
            sage: options = L.plot_parse_options()
            sage: alpha = L.simple_roots()
            sage: options.in_bounding_box(alpha[1])
            True
            sage: options.in_bounding_box(3*alpha[1])
            False
        """
        return self.bounding_box.contains(self.projection(x))

    def text(self, label, position, rgbcolor=(0,0,0)):
        r"""
        Return text widget with label ``label`` at position ``position``

        INPUT:

        - ``label`` -- a string, or a Sage object upon which latex will
          be called

        - ``position`` -- a position

        - ``rgbcolor`` -- the color as an RGB tuple

        EXAMPLES::

            sage: L = RootSystem(["A",2]).root_lattice()
            sage: options = L.plot_parse_options()
            sage: list(options.text("coucou", [0,1]))
            [Text 'coucou' at the point (0.0,1.0)]
            sage: list(options.text(L.simple_root(1), [0,1]))
            [Text '$\alpha_{1}$' at the point (0.0,1.0)]
            sage: list(options.text(L.simple_root(2), [1,0], rgbcolor=(1,0.5,0)))
            [Text '$\alpha_{2}$' at the point (1.0,0.0)]

            sage: options = RootSystem(["A",2]).root_lattice().plot_parse_options(labels=False)
            sage: options.text("coucou", [0,1])
            0

            sage: options = RootSystem(["B",3]).root_lattice().plot_parse_options()
            sage: print(options.text("coucou", [0,1,2]).x3d_str())
            <Transform translation='0 1 2'>
            <Shape><Text string='coucou' solid='true'/><Appearance><Material diffuseColor='0.0 0.0 0.0' shininess='1.0' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
            <BLANKLINE>
            </Transform>
        """
        if self.labels:
            if self.dimension <= 2:
                if not isinstance(label, str):
                    label = "$"+str(latex(label))+"$"
                from sage.plot.text import text
                return text(label, position, fontsize=15, rgbcolor=rgbcolor)
            elif self.dimension == 3:
                # LaTeX labels not yet supported in 3D
                if isinstance(label, str):
                    label = label.replace("{","").replace("}","").replace("$","").replace("_","")
                else:
                    label = str(label)
                from sage.plot.plot3d.shapes2 import text3d
                return text3d(label, position, rgbcolor=rgbcolor)
            else:
                raise NotImplementedError("Plots in dimension > 3")
        else:
            return self.empty()

    def index_of_object(self, i):
        """
        Try to return the node of the Dynkin diagram indexing the object `i`.

        OUTPUT: a node of the Dynkin diagram or ``None``

        EXAMPLES::

            sage: L = RootSystem(["A",3]).root_lattice()
            sage: alpha = L.simple_roots()
            sage: omega = RootSystem(["A",3]).weight_lattice().fundamental_weights()
            sage: options = L.plot_parse_options(labels=False)
            sage: options.index_of_object(3)
            3
            sage: options.index_of_object(alpha[1])
            1
            sage: options.index_of_object(omega[2])
            2
            sage: options.index_of_object(omega[2]+omega[3])
            sage: options.index_of_object(30)
            sage: options.index_of_object("bla")
        """
        if parent(i) in RootLatticeRealizations and  len(i) == 1 and i.leading_coefficient().is_one():
            i = i.leading_support()
        if i in self.space.cartan_type().index_set():
            return i
        return None

    def thickness(self, i):
        r"""
        Return the thickness to be used for lines indexed by `i`.

        INPUT:

        - ``i`` -- an index

        .. SEEALSO:: :meth:`index_of_object`

        EXAMPLES::

            sage: L = RootSystem(["A",2,1]).root_lattice()
            sage: options = L.plot_parse_options(labels=False)
            sage: alpha = L.simple_roots()
            sage: options.thickness(0)
            2
            sage: options.thickness(1)
            1
            sage: options.thickness(2)
            1
            sage: for alpha in L.simple_roots():
            ....:     print("{} {}".format(alpha, options.thickness(alpha)))
            alpha[0] 2
            alpha[1] 1
            alpha[2] 1
        """
        ct = self.space.cartan_type()
        if ct.is_affine() and ct.special_node() == self.index_of_object(i):
            return 2
        else:
            return 1

    def color(self, i):
        r"""
        Return the color to be used for objects indexed by `i`.

        INPUT:

        - ``i`` -- an index

        .. SEEALSO:: :meth:`index_of_object`

        EXAMPLES::

            sage: L = RootSystem(["A",2]).root_lattice()
            sage: options = L.plot_parse_options(labels=False)
            sage: alpha = L.simple_roots()
            sage: options.color(1)
            'blue'
            sage: options.color(2)
            'red'
            sage: for alpha in L.roots():
            ....:     print("{} {}".format(alpha, options.color(alpha)))
            alpha[1]             blue
            alpha[2]             red
            alpha[1] + alpha[2]  black
            -alpha[1]            black
            -alpha[2]            black
            -alpha[1] - alpha[2] black
        """
        return self._color(self.index_of_object(i))

    def projection(self, v):
        r"""
        Return the projection of ``v``.

        INPUT:

        - ``x`` -- an element of the root lattice realization

        OUTPUT:

        An immutable vector with integer or rational coefficients.

        EXAMPLES::

            sage: L = RootSystem(["A",2,1]).ambient_space()
            sage: options = L.plot_parse_options()
            sage: options.projection(L.rho())
            (0, 989/571)

            sage: options = L.plot_parse_options(projection=False)
            sage: options.projection(L.rho())
            (2, 1, 0)
        """
        for projection in self._projections:
            v = projection(v)
        v = vector(v)
        v.set_immutable()
        return v

    def intersection_at_level_1(self, x):
        r"""
        Return ``x`` scaled at the appropriate level, if level is set;
        otherwise return ``x``.

        INPUT:

        - ``x`` -- an element of the root lattice realization

        EXAMPLES::

            sage: L = RootSystem(["A",2,1]).weight_space()
            sage: options = L.plot_parse_options()
            sage: options.intersection_at_level_1(L.rho())
            1/3*Lambda[0] + 1/3*Lambda[1] + 1/3*Lambda[2]

            sage: options = L.plot_parse_options(affine=False, level=2)
            sage: options.intersection_at_level_1(L.rho())
            2/3*Lambda[0] + 2/3*Lambda[1] + 2/3*Lambda[2]

        When ``level`` is not set, ``x`` is returned::

            sage: options = L.plot_parse_options(affine=False)
            sage: options.intersection_at_level_1(L.rho())
            Lambda[0] + Lambda[1] + Lambda[2]

        """
        if self.level is not None:
            return x * self.level / x.level()
        else:
            return x

    def empty(self, *args):
        r"""
        Return an empty plot.

        EXAMPLES::

            sage: L = RootSystem(["A",2]).root_lattice()
            sage: options = L.plot_parse_options(labels=True)

        This currently returns ``int(0)``::

            sage: options.empty()
            0

        This is not a plot, so may cause some corner cases. On the
        other hand, `0` behaves as a fast neutral element, which is
        important given the typical idioms used in the plotting code::

            sage: p = point([0,0])
            sage: p + options.empty() is p
            True
        """
        return 0
        # if self.dimension == 2:
        #     from sage.plot.graphics import Graphics
        #     G = Graphics()
        # elif self.dimension == 3:
        #     from sage.plot.plot3d.base import Graphics3dGroup
        #     G = Graphics3dGroup()
        # else:
        #     assert False, "Dimension too high (or too low!)"
        # self.finalize(G)
        # return G

    def finalize(self, G):
        r"""
        Finalize a root system plot.

        INPUT:

        - ``G`` -- a root system plot or ``0``

        This sets the aspect ratio to 1 and remove the axes. This
        should be called by all the user-level plotting methods of
        root systems. This will become mostly obsolete when
        customization options won't be lost anymore upon addition of
        graphics objects and there will be a proper empty object for
        2D and 3D plots.

        EXAMPLES::

            sage: L = RootSystem(["B",2,1]).ambient_space()
            sage: options = L.plot_parse_options()
            sage: p = L.plot_roots(plot_options=options)
            sage: p += L.plot_coroots(plot_options=options)
            sage: p.axes()
            True
            sage: p = options.finalize(p)
            sage: p.axes()
            False
            sage: p.aspect_ratio()
            1.0

            sage: options = L.plot_parse_options(affine=False)
            sage: p = L.plot_roots(plot_options=options)
            sage: p += point([[1,1,0]])
            sage: p = options.finalize(p)
            sage: p.aspect_ratio()
            [1.0, 1.0, 1.0]

        If the input is ``0``, this returns an empty graphics object::

            sage: type(options.finalize(0))
            <class 'sage.plot.plot3d.base.Graphics3dGroup'>

            sage: options = L.plot_parse_options()
            sage: type(options.finalize(0))
            <class 'sage.plot.graphics.Graphics'>
            sage: list(options.finalize(0))
            []
        """
        from sage.plot.graphics import Graphics
        if self.dimension == 2:
            if G == 0:
                G = Graphics()
            G.set_aspect_ratio(1)
            # TODO: make this customizable
            G.axes(False)
        elif self.dimension == 3:
            if G == 0:
                from sage.plot.plot3d.base import Graphics3dGroup
                G = Graphics3dGroup()
            G.aspect_ratio(1)
            # TODO: Configuration axes
        return G

    def family_of_vectors(self, vectors):
        r"""
        Return a plot of a family of vectors.

        INPUT:

        - ``vectors`` -- family or vectors in ``self``

        The vectors are labelled by their index.

        EXAMPLES::

            sage: L = RootSystem(["A",2]).root_lattice()
            sage: options = L.plot_parse_options()
            sage: alpha = L.simple_roots()
            sage: p = options.family_of_vectors(alpha); p
            Graphics object consisting of 4 graphics primitives
            sage: list(p)
            [Arrow from (0.0,0.0) to (1.0,0.0),
             Text '$1$' at the point (1.05,0.0),
             Arrow from (0.0,0.0) to (0.0,1.0),
             Text '$2$' at the point (0.0,1.05)]

        Handling of colors and labels::

            sage: color=lambda i: "purple" if i==1 else None
            sage: options = L.plot_parse_options(labels=False, color=color)
            sage: p = options.family_of_vectors(alpha)
            sage: list(p)
            [Arrow from (0.0,0.0) to (1.0,0.0)]
            sage: p[0].options()['rgbcolor']
            'purple'

        Matplotlib emits a warning for arrows of length 0 and draws
        nothing anyway. So we do not draw them at all::

            sage: L = RootSystem(["A",2,1]).ambient_space()
            sage: options = L.plot_parse_options()
            sage: Lambda = L.fundamental_weights()
            sage: p = options.family_of_vectors(Lambda); p
            Graphics object consisting of 5 graphics primitives
            sage: list(p)
            [Text '$0$' at the point (0.0,0.0),
             Arrow from (0.0,0.0) to (0.5,0.86602451838...),
             Text '$1$' at the point (0.525,0.909325744308...),
             Arrow from (0.0,0.0) to (-0.5,0.86602451838...),
             Text '$2$' at the point (-0.525,0.909325744308...)]
        """
        from sage.plot.arrow import arrow
        tail = self.origin_projected
        G = self.empty()
        for i in vectors.keys():
            if self.color(i) is None:
                continue
            head = self.projection(vectors[i])
            if head != tail:
                G += arrow(tail, head, rgbcolor=self.color(i), arrowsize=self._arrowsize)
            G += self.text(i, 1.05*head)
        return self.finalize(G)

    def cone(self, rays=[], lines=[], color="black", thickness=1, alpha=1, wireframe=False,
             label=None, draw_degenerate=True, as_polyhedron=False):
        r"""
        Return the cone generated by the given rays and lines.

        INPUT:

        - ``rays``, ``lines`` -- lists of elements of the root lattice
          realization (default: ``[]``)

        - ``color`` -- a color (default: ``"black"``)

        - ``alpha`` -- a number in the interval `[0, 1]` (default: `1`)
          the desired transparency

        - ``label`` -- an object to be used as for this cone.
          The label itself will be constructed by calling
          :func:`~sage.misc.latex.latex` or :func:`repr` on the
          object depending on the graphics backend.

        - ``draw_degenerate`` -- a boolean (default: ``True``)
          whether to draw cones with a degenerate intersection with
          the bounding box

        - ``as_polyhedron`` -- a boolean (default: ``False``)
          whether to return the result as a polyhedron, without
          clipping it to the bounding box, and without making a plot
          out of it (for testing purposes)

        OUTPUT:

        A graphic object, a polyhedron, or ``0``.

        EXAMPLES::

            sage: L = RootSystem(["A",2]).root_lattice()
            sage: options = L.plot_parse_options()
            sage: alpha = L.simple_roots()
            sage: p = options.cone(rays=[alpha[1]], lines=[alpha[2]], color='green', label=2)
            sage: p
            Graphics object consisting of 2 graphics primitives
            sage: list(p)
            [Polygon defined by 4 points,
             Text '$2$' at the point (3.15...,3.15...)]
            sage: options.cone(rays=[alpha[1]], lines=[alpha[2]], color='green', label=2, as_polyhedron=True)
            A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line

        An empty result, being outside of the bounding box::

            sage: options = L.plot_parse_options(labels=True, bounding_box=[[-10,-9]]*2)
            sage: options.cone(rays=[alpha[1]], lines=[alpha[2]], color='green', label=2)
            0

        Test that the options are properly passed down::

            sage: L = RootSystem(["A",2]).root_lattice()
            sage: options = L.plot_parse_options()
            sage: p = options.cone(rays=[alpha[1]+alpha[2]], color='green', label=2, thickness=4, alpha=.5)
            sage: list(p)
            [Line defined by 2 points, Text '$2$' at the point (3.15...,3.15...)]
            sage: sorted(p[0].options().items())
            [('alpha', 0.500000000000000), ('legend_color', None),
             ('legend_label', None), ('rgbcolor', 'green'), ('thickness', 4),
             ('zorder', 1)]

        This method is tested indirectly but extensively by the
        various plot methods of root lattice realizations.
        """
        if color is None:
            return self.empty()
        from sage.geometry.polyhedron.all import Polyhedron
        # TODO: we currently convert lines into rays, which simplify a
        # bit the calculation of the intersection. But it would be
        # nice to benefit from the new ``lines`` option of Polyhedra
        rays = list(rays) + [ray for ray in lines] + [-ray for ray in lines]

        # Compute the intersection at level 1, if needed
        if self.level:
            old_rays = rays
            vertices = [self.intersection_at_level_1(ray) for ray in old_rays if ray.level() > 0]
            rays     = [ray for ray in old_rays if ray.level() == 0]
            rays    += [vertex - self.intersection_at_level_1(ray) for ray in old_rays if ray.level() < 0 for vertex in vertices]
        else:
            vertices = []

        # Apply the projection (which is supposed to be affine)
        vertices = [ self.projection(vertex) for vertex in vertices ]
        rays = [ self.projection(ray)-self.projection(self.space.zero()) for ray in rays ]
        rays = [ ray for ray in rays if ray ] # Polyhedron does not accept yet zero rays

        # Build the polyhedron
        p = Polyhedron(vertices=vertices, rays = rays)
        if as_polyhedron:
            return p

        # Compute the intersection with the bounding box
        q = p & self.bounding_box
        if q.dim() >= 0 and p.dim() >= 0 and (draw_degenerate or p.dim()==q.dim()):
            if wireframe:
                options = dict(point=False, line=dict(width=10), polygon=False)
                center = q.center()
                q = q.translation(-center).dilation(ZZ(95)/ZZ(100)).translation(center)
            else:
                options = dict(wireframe=False, line={"thickness":thickness})
            result = q.plot(color = color, alpha=alpha, **options)
            if label is not None:
                # Put the label on the vertex having largest z, then y, then x coordinate.
                vertices = sorted([vector(v) for v in q.vertices()],
                                  key=lambda x: list(reversed(x)))
                result += self.text(label, 1.05*vector(vertices[-1]))
            return result
        else:
            return self.empty()

    def reflection_hyperplane(self, coroot, as_polyhedron=False):
        r"""
        Return a plot of the reflection hyperplane indexed by this coroot.

        - ``coroot`` -- a coroot

        EXAMPLES::

            sage: L = RootSystem(["B",2]).weight_space()
            sage: alphacheck = L.simple_coroots()
            sage: options = L.plot_parse_options()
            sage: H = options.reflection_hyperplane(alphacheck[1]); H
            Graphics object consisting of 2 graphics primitives

        TESTS::

            sage: print(H.description())
            Text '$H_{\alpha^\vee_{1}}$' at the point (0.0,3.15...)
            Line defined by 2 points: [(0.0, 3.0), (0.0, -3.0)]

        ::

            sage: L = RootSystem(["A",3,1]).ambient_space()
            sage: alphacheck = L.simple_coroots()
            sage: options = L.plot_parse_options()
            sage: H = options.reflection_hyperplane(alphacheck[1], as_polyhedron=True); H
            A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 2 lines
            sage: H.lines()
            (A line in the direction (0, 0, 1), A line in the direction (0, 1, 0))
            sage: H.vertices()
            (A vertex at (0, 0, 0),)

        ::

            sage: all(options.reflection_hyperplane(c, as_polyhedron=True).dim() == 2
            ....:     for c in alphacheck)
            True


        .. TODO::

            Display the periodic orientation by adding a `+` and
            a `-` sign close to the label. Typically by using
            the associated root to shift a bit from the vertex
            upon which the hyperplane label is attached.
        """
        from sage.matrix.constructor import matrix
        L = self.space
        label = coroot
        # scalar currently only handles scalar product with
        # elements of self.coroot_lattice(). Furthermore, the
        # latter is misnamed: for ambient spaces, this does
        # not necessarily coincide with the coroot lattice of
        # the rootsystem. So we need to do a coercion.
        coroot = self.space.coroot_lattice()(coroot)
        # Compute the kernel of the linear form associated to the coroot
        vectors = matrix([b.scalar(coroot) for b in L.basis()]).right_kernel().basis()
        basis = [L.from_vector(v) for v in vectors]
        if self.dimension == 3: # LaTeX labels not yet supported in 3D
            text_label = "H_%s$"%(str(label))
        else:
            text_label = "$H_{%s}$"%(latex(label))
        return self.cone(lines = basis, color = self.color(label), label=text_label,
                         as_polyhedron=as_polyhedron)


@cached_function
def barycentric_projection_matrix(n, angle=0):
    r"""
    Returns a family of `n+1` vectors evenly spaced in a real vector space of dimension `n`

    Those vectors are of norm `1`, the scalar product between any two
    vector is `1/n`, thus the distance between two tips is constant.

    The family is built recursively and uniquely determined by the
    following property: the last vector is `(0,\dots,0,-1)`, and the
    projection of the first `n` vectors in dimension `n-1`, after
    appropriate rescaling to norm `1`, retrieves the family for `n-1`.

    OUTPUT:

    A matrix with `n+1` columns of height `n` with rational or
    symbolic coefficients.

    EXAMPLES:

    One vector in dimension `0`::

        sage: from sage.combinat.root_system.root_lattice_realizations import barycentric_projection_matrix
        sage: m = barycentric_projection_matrix(0); m
        []
        sage: matrix(QQ,0,1).nrows()
        0
        sage: matrix(QQ,0,1).ncols()
        1

    Two vectors in dimension 1::

        sage: barycentric_projection_matrix(1)
        [ 1 -1]

    Three vectors in dimension 2::

        sage: barycentric_projection_matrix(2)
        [ 1/2*sqrt(3) -1/2*sqrt(3)            0]
        [         1/2          1/2           -1]

    Four vectors in dimension 3::

        sage: m = barycentric_projection_matrix(3); m
        [ 1/3*sqrt(3)*sqrt(2) -1/3*sqrt(3)*sqrt(2)                    0                    0]
        [         1/3*sqrt(2)          1/3*sqrt(2)         -2/3*sqrt(2)                    0]
        [                 1/3                  1/3                  1/3                   -1]

    The columns give four vectors that sum up to zero::

        sage: sum(m.columns())
        (0, 0, 0)

    and have regular mutual angles::

        sage: m.transpose()*m
        [   1 -1/3 -1/3 -1/3]
        [-1/3    1 -1/3 -1/3]
        [-1/3 -1/3    1 -1/3]
        [-1/3 -1/3 -1/3    1]

    Here is a plot of them::

        sage: sum(arrow((0,0,0),x) for x in m.columns())
        Graphics3d Object

    For 2D drawings of root systems, it is desirable to rotate the
    result to match with the usual conventions::

        sage: barycentric_projection_matrix(2, angle=2*pi/3)
        [         1/2           -1          1/2]
        [ 1/2*sqrt(3)            0 -1/2*sqrt(3)]

    TESTS::

        sage: for n in range(1, 7):
        ....:     m = barycentric_projection_matrix(n)
        ....:     assert sum(m.columns()).is_zero()
        ....:     assert matrix(QQ, n+1,n+1, lambda i,j: 1 if i==j else -1/n) == m.transpose()*m

    """
    from sage.matrix.constructor import matrix
    from sage.misc.functional import sqrt
    n = ZZ(n)
    if n == 0:
        return matrix(QQ, 0, 1)
    a = 1/n
    b = sqrt(1-a**2)
    result = b * barycentric_projection_matrix(n-1)
    result = result.augment(vector([0]*(n-1)))
    result = result.stack(matrix([[a]*n+[-1]]))
    assert sum(result.columns()).is_zero()
    if angle and n == 2:
        from sage.functions.trig import sin
        from sage.functions.trig import cos
        rotation = matrix([[sin(angle), cos(angle)],
                           [-cos(angle), sin(angle)]])
        result = rotation * result
    result.set_immutable()
    return result
